home *** CD-ROM | disk | FTP | other *** search
/ Revista do CD-ROM 97 / CD-ROM 97 / CD-ROM 97.iso / internet / ghostzilla / ghsetup.exe / chrome / comm.jar / content / navigator / personalToolbar.js < prev    next >
Encoding:
JavaScript  |  2002-04-10  |  19.2 KB  |  507 lines

  1. /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: NPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Netscape Public License
  6.  * Version 1.1 (the "License"); you may not use this file except in
  7.  * compliance with the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/NPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is mozilla.org code.
  16.  *
  17.  * The Initial Developer of the Original Code is 
  18.  * Netscape Communications Corporation.
  19.  * Portions created by the Initial Developer are Copyright (C) 1998
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *   Ben Goodger <ben@netscape.com> (Original Author)
  24.  *
  25.  * Alternatively, the contents of this file may be used under the terms of
  26.  * either the GNU General Public License Version 2 or later (the "GPL"), or 
  27.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28.  * in which case the provisions of the GPL or the LGPL are applicable instead
  29.  * of those above. If you wish to allow use of your version of this file only
  30.  * under the terms of either the GPL or the LGPL, and not to allow others to
  31.  * use your version of this file under the terms of the NPL, indicate your
  32.  * decision by deleting the provisions above and replace them with the notice
  33.  * and other provisions required by the GPL or the LGPL. If you do not delete
  34.  * the provisions above, a recipient may use your version of this file under
  35.  * the terms of any one of the NPL, the GPL or the LGPL.
  36.  *
  37.  * ***** END LICENSE BLOCK ***** */
  38.  
  39. var gBookmarksShell = null;
  40.  
  41. ///////////////////////////////////////////////////////////////////////////////
  42. // Class which defines methods for a bookmarks UI implementation based around
  43. // a toolbar. Subclasses BookmarksBase in bookmarksOverlay.js. Some methods
  44. // are required by the base class, others are for event handling. Window specific
  45. // glue code should go into the BookmarksWindow class in bookmarks.js
  46. function BookmarksToolbar (aID)
  47. {
  48.   this.id = aID;
  49. }
  50.  
  51. BookmarksToolbar.prototype = {
  52.   __proto__: BookmarksUIElement.prototype,
  53.  
  54.   /////////////////////////////////////////////////////////////////////////////
  55.   // Personal Toolbar Specific Stuff
  56.   
  57.   get db ()
  58.   {
  59.     return this.element.database;
  60.   },
  61.  
  62.   get element ()
  63.   {
  64.     return document.getElementById(this.id);
  65.   },
  66.  
  67.   /////////////////////////////////////////////////////////////////////////////
  68.   // This method constructs a menuitem for a context menu for the given command.
  69.   // This is implemented by the client so that it can intercept menuitem naming
  70.   // as appropriate.
  71.   createMenuItem: function (aDisplayName, aCommandName, aItemNode)
  72.   {
  73.     const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  74.     var xulElement = document.createElementNS(kXULNS, "menuitem");
  75.     xulElement.setAttribute("cmd", aCommandName);
  76.     var cmd = "cmd_" + aCommandName.substring(NC_NS_CMD.length)
  77.     xulElement.setAttribute("command", cmd);
  78.     
  79.     switch (aCommandName) {
  80.     case NC_NS_CMD + "bm_open":
  81.       xulElement.setAttribute("label", aDisplayName);
  82.       xulElement.setAttribute("default", "true");
  83.       break;
  84.     case NC_NS_CMD + "bm_openfolder":
  85.       xulElement.setAttribute("default", "true");
  86.       if (aItemNode.localName == "hbox") 
  87.         // Don't show an "Open Folder" item for clicks on the toolbar itself.
  88.         return null;
  89.     default:
  90.       xulElement.setAttribute("label", aDisplayName);
  91.       break;
  92.     }
  93.     return xulElement;
  94.   },
  95.  
  96.   // Command implementation
  97.   commands: {
  98.     openFolder: function (aSelectedItem)
  99.     {
  100.       var mbo = aSelectedItem.boxObject.QueryInterface(Components.interfaces.nsIMenuBoxObject);
  101.       mbo.openMenu(true);
  102.     },
  103.  
  104.     editCell: function (aSelectedItem, aXXXLameAssIndex)
  105.     {
  106.       goDoCommand("cmd_bm_properties");
  107.       return; // Disable Inline Edit for now. See bug 77125 for why this is being disabled
  108.               // on the personal toolbar for the moment. 
  109.  
  110.       if (aSelectedItem.getAttribute("editable") != "true")
  111.         return;
  112.       var property = "http://home.netscape.com/NC-rdf#Name";
  113.       aSelectedItem.setMode("edit");
  114.       aSelectedItem.addObserver(this.postModifyCallback, "accept", 
  115.                                 [gBookmarksShell, aSelectedItem, property]);
  116.     },
  117.  
  118.     ///////////////////////////////////////////////////////////////////////////
  119.     // Called after an inline-edit cell has left inline-edit mode, and data
  120.     // needs to be modified in the datasource.
  121.     postModifyCallback: function (aParams)
  122.     {
  123.       var aShell = aParams[0];
  124.       var selItemURI = NODE_ID(aParams[1]);
  125.       aShell.propertySet(selItemURI, aParams[2], aParams[3]);
  126.     },
  127.  
  128.     ///////////////////////////////////////////////////////////////////////////
  129.     // Creates a dummy item that can be placed in edit mode to retrieve data
  130.     // to create new bookmarks/folders.
  131.     createBookmarkItem: function (aMode, aSelectedItem)
  132.     {
  133.       /////////////////////////////////////////////////////////////////////////
  134.       // HACK HACK HACK HACK HACK         
  135.       // Disable Inline-Edit for now and just use a dialog. 
  136.       
  137.       // XXX - most of this is just copy-pasted from the other two folder
  138.       //       creation functions. Yes it's ugly, but it'll do the trick for 
  139.       //       now as this is in no way intended to be a long-term solution.
  140.  
  141.       const kPromptSvcContractID = "@mozilla.org/embedcomp/prompt-service;1";
  142.       const kPromptSvcIID = Components.interfaces.nsIPromptService;
  143.       const kPromptSvc = Components.classes[kPromptSvcContractID].getService(kPromptSvcIID);
  144.       
  145.       var defaultValue  = gBookmarksShell.getLocaleString("ile_newfolder");
  146.       var dialogTitle   = gBookmarksShell.getLocaleString("newfolder_dialog_title");
  147.       var dialogMsg     = gBookmarksShell.getLocaleString("newfolder_dialog_msg");
  148.       var stringValue   = { value: defaultValue };
  149.       if (kPromptSvc.prompt(window, dialogTitle, dialogMsg, stringValue, null, { value: 0 })) {
  150.         var relativeNode = aSelectedItem || gBookmarksShell.element;
  151.         var parentNode = relativeNode ? gBookmarksShell.findRDFNode(relativeNode, false) : gBookmarksShell.element;
  152.  
  153.         var args = [{ property: NC_NS + "parent",
  154.                       resource: NODE_ID(parentNode) },
  155.                     { property: NC_NS + "Name",
  156.                       literal:  stringValue.value }];
  157.         
  158.         const kBMDS = gBookmarksShell.RDF.GetDataSource("rdf:bookmarks");
  159.         var relId = relativeNode ? NODE_ID(relativeNode) : "NC:PersonalToolbarFolder";
  160.         BookmarksUtils.doBookmarksCommand(relId, NC_NS_CMD + "newfolder", args);
  161.       }
  162.       
  163.       return; 
  164.       
  165.       // HACK HACK HACK HACK HACK         
  166.       /////////////////////////////////////////////////////////////////////////
  167.       
  168.       const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  169.       var dummyButton = document.createElementNS(kXULNS, "menubutton");
  170.       dummyButton = gBookmarksShell.createBookmarkFolderDecorations(dummyButton);
  171.       dummyButton.setAttribute("class", "button-toolbar bookmark-item");
  172.  
  173.       dummyButton.setAttribute("label", gBookmarksShell.getLocaleString("ile_newfolder") + "  ");
  174.       // By default, create adjacent to the selected button. If there is no button after
  175.       // the selected button, or the target is the toolbar itself, just append. 
  176.       var bIsButton = aSelectedItem.localName == "button" || aSelectedItem.localName == "menubutton";
  177.       if (aSelectedItem.nextSibling && bIsButton)
  178.         aSelectedItem.parentNode.insertBefore(dummyButton, aSelectedItem.nextSibling);
  179.       else
  180.         (bIsButton ? aSelectedItem.parentNode : aSelectedItem).appendChild(dummyButton);
  181.  
  182.       gBookmarksShell._focusElt = document.commandDispatcher.focusedElement;
  183.       dummyButton.setMode("edit");
  184.       // |aSelectedItem| will be the node we create the new folder relative to. 
  185.       dummyButton.addObserver(this.onEditFolderName, "accept", 
  186.                               [dummyButton, aSelectedItem, dummyButton]);
  187.       dummyButton.addObserver(this.onEditFolderName, "reject", 
  188.                               [dummyButton, aSelectedItem, dummyButton]);
  189.     },
  190.  
  191.     ///////////////////////////////////////////////////////////////////////////
  192.     // Edit folder name & update the datasource if name is valid
  193.     onEditFolderName: function (aParams, aTopic)
  194.     {
  195.       // Because the toolbar has no concept of selection, this function
  196.       // is much simpler than the one in bookmarksTree.js. However it may
  197.       // become more complex if pink ever lets me put context menus on menus ;) 
  198.       var name = aParams[3];
  199.       var dummyButton = aParams[2];
  200.       var relativeNode = aParams[1];
  201.       var parentNode = gBookmarksShell.findRDFNode(relativeNode, false);
  202.  
  203.       dummyButton.parentNode.removeChild(dummyButton);
  204.  
  205.       if (!gBookmarksShell.commands.validateNameAndTopic(name, aTopic, relativeNode, dummyButton))
  206.         return;
  207.  
  208.       parentNode = relativeNode.parentNode;
  209.       if (relativeNode.localName == "hbox") {
  210.         parentNode = relativeNode;
  211.         relativeNode = (gBookmarksShell.commands.nodeIsValidType(relativeNode) && 
  212.                         relativeNode.lastChild) || relativeNode;
  213.       }
  214.  
  215.       var args = [{ property: NC_NS + "parent",
  216.                     resource: NODE_ID(parentNode) },
  217.                   { property: NC_NS + "Name",
  218.                     literal:  name }];
  219.  
  220.       BookmarksUtils.doBookmarksCommand(NODE_ID(relativeNode),
  221.                                         NC_NS_CMD + "newfolder", args);
  222.       // We need to do this because somehow focus shifts and no commands 
  223.       // operate any more. 
  224.       //gBookmarksShell._focusElt.focus();
  225.     },
  226.  
  227.     nodeIsValidType: function (aNode)
  228.     {
  229.       switch (aNode.localName) {
  230.       case "button":
  231.       case "menubutton":
  232.       // case "menu":
  233.       // case "menuitem":
  234.         return true;
  235.       }
  236.       return false;
  237.     },
  238.  
  239.     ///////////////////////////////////////////////////////////////////////////
  240.     // Performs simple validation on what the user has entered:
  241.     //  1) prevents entering an empty string
  242.     //  2) in the case of a canceled operation, remove the dummy item and
  243.     //     restore selection.
  244.     validateNameAndTopic: function (aName, aTopic, aOldSelectedItem, aDummyItem)
  245.     {
  246.       // Don't allow user to enter an empty string "";
  247.       if (!aName) return false;
  248.  
  249.       // If the user hit escape, go no further.
  250.       return !(aTopic == "reject");
  251.     }
  252.   },
  253.  
  254.   _focusElt: null,
  255.  
  256.   /////////////////////////////////////////////////////////////////////////////
  257.   // Evaluates an event to determine whether or not it affords opening a tree
  258.   // item. Typically, this is when the left mouse button is used, and provided
  259.   // the click-rate matches that specified by our owning tree class. For example,
  260.   // some trees open an item when double clicked (bookmarks/history windows) and
  261.   // others on a single click (sidebar panels).
  262.   isValidOpenEvent: function (aEvent)
  263.   {
  264.     return !(aEvent.type == "click" &&
  265.              (aEvent.button != 0 || aEvent.detail != this.openClickCount))
  266.   },
  267.  
  268.   /////////////////////////////////////////////////////////////////////////////
  269.   // For the given selection, selects the best adjacent element. This method is
  270.   // useful when an action such as a cut or a deletion is performed on a
  271.   // selection, and focus/selection needs to be restored after the operation
  272.   // is performed.
  273.   getNextElement: function (aElement)
  274.   {
  275.     if (aElement.nextSibling)
  276.       return aElement.nextSibling;
  277.     else if (aElement.previousSibling)
  278.       return aElement.previousSibling;
  279.     else
  280.       return aElement.parentNode;
  281.   },
  282.  
  283.   selectElement: function (aElement)
  284.   {
  285.   },
  286.  
  287.   //////////////////////////////////////////////////////////////////////////////
  288.   // Add the treeitem element specified by aURI to the tree's current selection.
  289.   addItemToSelection: function (aURI)
  290.   {
  291.   },
  292.  
  293.   /////////////////////////////////////////////////////////////////////////////
  294.   // Return a set of DOM nodes that represents the current item in the Bookmarks
  295.   // Toolbar. This is always |document.popupNode|.
  296.   getSelection: function ()
  297.   {
  298.     return [document.popupNode];
  299.   },
  300.  
  301.   /////////////////////////////////////////////////////////////////////////////
  302.   // Return a set of DOM nodes that represent the selection in the tree widget.
  303.   // This method is takes a node parameter which is the popupNode for the
  304.   // document. If the popupNode is not contained by the selection, the
  305.   // popupNode is selected and the new selection returned.
  306.   getContextSelection: function (aItemNode)
  307.   {
  308.     return [aItemNode];
  309.   },
  310.  
  311.   getSelectedFolder: function ()
  312.   {
  313.     return "NC:PersonalToolbarFolder";
  314.   },
  315.  
  316.   /////////////////////////////////////////////////////////////////////////////
  317.   // For a given start DOM element, find the enclosing DOM element that contains
  318.   // the template builder RDF resource decorations (id, ref, etc). In the 
  319.   // Toolbar case, this is always the popup node (until we're proven wrong ;)
  320.   findRDFNode: function (aStartNode, aIncludeStartNodeFlag)
  321.   {
  322.     var temp = aStartNode;
  323.     while (temp && temp.localName != (aIncludeStartNodeFlag ? "toolbarbutton" : "hbox")) 
  324.       temp = temp.parentNode;
  325.     return temp || this.element;
  326.   },
  327.  
  328.   selectFolderItem: function (aFolderURI, aItemURI, aAdditiveFlag)
  329.   {
  330.     var folder = document.getElementById(aFolderURI);
  331.     var kids = ContentUtils.childByLocalName(folder, "treechildren");
  332.     if (!kids) return;
  333.  
  334.     var item = kids.firstChild;
  335.     while (item) {
  336.       if (item.id == aItemURI) break;
  337.       item = item.nextSibling;
  338.     }
  339.     if (!item) return;
  340.  
  341.     this.tree[aAdditiveFlag ? "addItemToSelection" : "selectItem"](item);
  342.   },
  343.  
  344.   /////////////////////////////////////////////////////////////////////////////
  345.   // Command handling & Updating.
  346.   controller: {
  347.     supportsCommand: function (aCommand)
  348.     {
  349.       switch(aCommand) {
  350.       case "cmd_bm_undo":
  351.       case "cmd_bm_redo":
  352.         return false;
  353.       case "cmd_bm_cut":
  354.       case "cmd_bm_copy":
  355.       case "cmd_bm_paste":
  356.       case "cmd_bm_delete":
  357.       case "cmd_bm_selectAll":
  358.       case "cmd_bm_open":
  359.       case "cmd_bm_openfolder":
  360.       case "cmd_bm_openinnewwindow":
  361.       case "cmd_bm_newbookmark":
  362.       case "cmd_bm_newfolder":
  363.       case "cmd_bm_newseparator":
  364.       case "cmd_bm_find":
  365.       case "cmd_bm_properties":
  366.       case "cmd_bm_rename":
  367.       case "cmd_bm_setnewbookmarkfolder":
  368.       case "cmd_bm_setpersonaltoolbarfolder":
  369.       case "cmd_bm_setnewsearchfolder":
  370.       case "cmd_bm_import":
  371.       case "cmd_bm_export":
  372.       case "cmd_bm_fileBookmark":
  373.         return true;
  374.       default:
  375.         return false;
  376.       }
  377.     },
  378.  
  379.     isCommandEnabled: function (aCommand)
  380.     {
  381.       switch(aCommand) {
  382.       case "cmd_bm_undo":
  383.       case "cmd_bm_redo":
  384.         return false;
  385.       case "cmd_bm_paste":
  386.         var cp = gBookmarksShell.canPaste();
  387.         return cp;
  388.       case "cmd_bm_cut":
  389.       case "cmd_bm_copy":
  390.       case "cmd_bm_delete":
  391.         return (document.popupNode != null) && (NODE_ID(document.popupNode) != "NC:PersonalToolbarFolder");
  392.       case "cmd_bm_selectAll":
  393.         return false;
  394.       case "cmd_bm_open":
  395.         var seln = gBookmarksShell.getSelection();
  396.         return document.popupNode != null && seln[0].getAttributeNS(RDF_NS, "type") == NC_NS + "Bookmark";
  397.       case "cmd_bm_openfolder":
  398.         seln = gBookmarksShell.getSelection();
  399.         return document.popupNode != null && seln[0].getAttributeNS(RDF_NS, "type") == NC_NS + "Folder";
  400.       case "cmd_bm_openinnewwindow":
  401.         return true;
  402.       case "cmd_bm_find":
  403.       case "cmd_bm_newbookmark":
  404.       case "cmd_bm_newfolder":
  405.       case "cmd_bm_newseparator":
  406.       case "cmd_bm_import":
  407.       case "cmd_bm_export":
  408.         return true;
  409.       case "cmd_bm_properties":
  410.       case "cmd_bm_rename":
  411.         return document.popupNode != null;
  412.       case "cmd_bm_setnewbookmarkfolder":
  413.         seln = gBookmarksShell.getSelection();
  414.         if (!seln.length) return false;
  415.         var folderType = seln[0].getAttributeNS(RDF_NS, "type") == (NC_NS + "Folder");
  416.         return document.popupNode != null && !(NODE_ID(seln[0]) == "NC:NewBookmarkFolder") && folderType;
  417.       case "cmd_bm_setpersonaltoolbarfolder":
  418.         seln = gBookmarksShell.getSelection();
  419.         if (!seln.length) return false;
  420.         folderType = seln[0].getAttributeNS(RDF_NS, "type") == (NC_NS + "Folder");
  421.         return document.popupNode != null && !(NODE_ID(seln[0]) == "NC:PersonalToolbarFolder") && folderType;
  422.       case "cmd_bm_setnewsearchfolder":
  423.         seln = gBookmarksShell.getSelection();
  424.         if (!seln.length) return false;
  425.         folderType = seln[0].getAttributeNS(RDF_NS, "type") == (NC_NS + "Folder");
  426.         return document.popupNode != null && !(NODE_ID(seln[0]) == "NC:NewSearchFolder") && folderType;
  427.       case "cmd_bm_fileBookmark":
  428.         seln = gBookmarksShell.getSelection();
  429.         return seln.length > 0;
  430.       default:
  431.         return false;
  432.       }
  433.     },
  434.  
  435.     doCommand: function (aCommand)
  436.     {
  437.       switch(aCommand) {
  438.       case "cmd_bm_undo":
  439.       case "cmd_bm_redo":
  440.         break;
  441.       case "cmd_bm_paste":
  442.       case "cmd_bm_copy":
  443.       case "cmd_bm_cut":
  444.       case "cmd_bm_delete":
  445.       case "cmd_bm_newbookmark":
  446.       case "cmd_bm_newfolder":
  447.       case "cmd_bm_newseparator":
  448.       case "cmd_bm_properties":
  449.       case "cmd_bm_rename":
  450.       case "cmd_bm_open":
  451.       case "cmd_bm_openfolder":
  452.       case "cmd_bm_openinnewwindow":
  453.       case "cmd_bm_setnewbookmarkfolder":
  454.       case "cmd_bm_setpersonaltoolbarfolder":
  455.       case "cmd_bm_setnewsearchfolder":
  456.       case "cmd_bm_find":
  457.       case "cmd_bm_import":
  458.       case "cmd_bm_export":
  459.       case "cmd_bm_fileBookmark":
  460.         gBookmarksShell.execCommand(aCommand.substring("cmd_".length));
  461.         break;
  462.       case "cmd_bm_selectAll":
  463.         break;
  464.       }
  465.     },
  466.  
  467.     onEvent: function (aEvent)
  468.     {
  469.     },
  470.  
  471.     onCommandUpdate: function ()
  472.     {
  473.     }
  474.   }
  475. };
  476.  
  477. function BM_navigatorLoad(aEvent)
  478. {
  479.   if (!gBookmarksShell) {
  480.     gBookmarksShell = new BookmarksToolbar("innermostBox");
  481.     controllers.appendController(gBookmarksShell.controller);
  482.     removeEventListener("load", BM_navigatorLoad, false);
  483.   }
  484. }
  485.  
  486.  
  487. // An interim workaround for 101131 - Bookmarks Toolbar button nonfunctional.
  488. // This simply checks to see if the bookmark menu is empty (aside from static
  489. // items) when it is opened and if it is, prompts a rebuild. 
  490. // The best fix for this is more time consuming, and relies on document
  491. // <template>s without content (referencing a remote <template/> by id) 
  492. // be noted as 'waiting' for a template to load from somewhere. When the 
  493. // ::Merge function in nsXULDocument is called and a template node inserted, 
  494. // the id of the template to be inserted is looked up in the map of waiting
  495. // references, and then the template builder hooked up. 
  496. function checkBookmarksMenuTemplateBuilder()
  497. {
  498.   var lastStaticSeparator = document.getElementById("lastStaticSeparator");
  499.   if (!lastStaticSeparator.nextSibling) {
  500.     var button = document.getElementById("bookmarks-button");
  501.     button.builder.rebuild();
  502.   }
  503. }
  504.  
  505. addEventListener("load", BM_navigatorLoad, false);
  506.  
  507.